package com.zhou.common.quartz.test.timewheel;

import cn.hutool.core.date.SystemClock;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.concurrent.DelayQueue;

/**
 * @author xianfeng
 * @date 2023/12/16 9:54
 * @description: 时间轮
 */


@Data
@Slf4j
public class TimeWheel {

    /**
     * 基本时间跨度
     */
    private long timeSpan;
    /**
     * 时间单位个数
     */
    private int wheelSize;
    /**
     * 总体时间跨度
     */
    private long overallTime;
    /**
     * 定时任务列表
     */
    private TimerTaskList[] buckets;
    /**
     * 上层时间轮
     */
    private volatile TimeWheel overflowWheel;
    private volatile TimeWheel prefixWheel;
    /**
     * 一个Timer只有一个DelayQueue,协助推进时间轮
     */
    private DelayQueue<TimerTaskList> delayQueue;

    private int level;

    public TimeWheel(long timeSpan, int wheelSize, DelayQueue<TimerTaskList> delayQueue) {
        this(1, timeSpan, wheelSize, delayQueue, null);
    }

    public TimeWheel(int level, long timeSpan, int wheelSize, DelayQueue<TimerTaskList> delayQueue, TimeWheel prefixWheel) {
        this.timeSpan = timeSpan;
        this.wheelSize = wheelSize;
        this.overallTime = timeSpan * wheelSize;
        this.buckets = new TimerTaskList[wheelSize];
        this.delayQueue = delayQueue;
        this.level = level;
        this.prefixWheel = prefixWheel;
        for (int i = 0; i < wheelSize; i++) {
            buckets[i] = new TimerTaskList(level, i);
        }
    }

    public boolean add(TimerTaskEntry entry) {
        long expiration = entry.getExpireMs();
        if (expiration < timeSpan + SystemClock.now()) {
            log.debug("add 时，直接过期expiration={},currentTime={},currTimeSpan={},overallTime={}", expiration, SystemClock.now(), timeSpan, overallTime);
            // 定时任务到期
            if (level == 1) {
                return false;
            } else {
                return prefixWheel.add(entry);
            }
        } else if (expiration < SystemClock.now() + overallTime) {
            // 扔进当前时间轮的某个槽里,只有时间大于某个槽,才会放进去
            long virtualId = (expiration / timeSpan);
            int index = (int) (virtualId % wheelSize);
            TimerTaskList bucket = buckets[index];
            bucket.addTask(entry);
            log.debug("add 时，当前层成功 level={},bucketIndex={},taskCount={},", level, index, bucket.getTaskCount());
            // 设置bucket 过期时间
            if (bucket.setExpiration(virtualId * timeSpan)) {
                // 设好过期时间的bucket需要入队
                delayQueue.offer(bucket);
            }
            return true;
            /*log.info("{}", bucket);
            if (delayQueue.contains(bucket)) {
                bucket.addTask(entry);
                log.info("contains,level={},taskCount={},index={},currentTime={}", level, bucket.getTaskCount(), index, SystemClock.now());
                bucket.setExpiration(virtualId * timeSpan);
                *//*if (bucket.setExpiration(virtualId * timeSpan)) {
                    delayQueue.offer(bucket);
                }*//*
            } else {
                buckets[index] = new TimerTaskList(level, index);
                bucket.addTask(entry);
                log.info("level={},taskCount={},index={},currentTime={}", level, bucket.getTaskCount(), index, SystemClock.now());
                bucket.setExpiration(virtualId * timeSpan);
                delayQueue.offer(bucket);
            }
            return true;*/
        } else {
            log.debug("add 时，扔到上一轮,expiration={},currentTime={},currTimeSpan={},overallTime={}", expiration, timeSpan, overallTime);
            // 当前轮不能满足,需要扔到上一轮
            TimeWheel timeWheel = getOverflowWheel();
            return timeWheel.add(entry);
        }
        /*log.error("add 时，默认返回为false,entry={},expiration={},currentTime={},currTimeSpan={},overallTime={}", entry.getTimerTask()
                .getDesc(), expiration, currentTime, timeSpan, overallTime);
        return false;*/
    }

    private TimeWheel getOverflowWheel() {
        if (overflowWheel == null) {
            synchronized (this) {
                if (overflowWheel == null) {
                    overflowWheel = new TimeWheel(level + 1, overallTime, wheelSize, delayQueue, this);
                }
            }
        }
        return overflowWheel;
    }

    @Override
    public String toString() {
        return "TimeWheel{" +
                "timeSpan=" + timeSpan +
                ", wheelSize=" + wheelSize +
                ", overallTime=" + overallTime +
                ", buckets=" + Arrays.toString(buckets) +
                ", delayQueue=" + delayQueue +
                '}';
    }
}
